/*
    libnbds
    Non-blocking Data Structures Library

    Copyright (C) 2011 Paweł Dziepak

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "atomic.h"
#include "hazard_pointer.h"

using namespace nbds;

hazard_pointer_base::hp_ticket *hazard_pointer_base::get_ticket() {
	if (ticket.exists())
		return ticket.ptr();
	else {
		hp_ticket *t = ticket.ptr();
		t->ptr = NULL;

		do {
			t->prev = arch::get(&last);
		} while (arch::cmp_and_swp(&last, t->prev, t) != t->prev);

		return t;
	}
}

bool hazard_pointer_base::try_retire(void *obj) {
	hp_ticket *current = arch::get(&last);
	while (current) {
		if (arch::get(&current->ptr) == obj)
			return false;

		current = arch::get(&current->prev);
	}

	destr(obj);
	return true;
}

void hazard_pointer_base::set(void *obj) {
	arch::set(&get_ticket()->ptr, obj);
}

void hazard_pointer_base::release() {
	arch::set(&get_ticket()->ptr, (void*)0);
}

void hazard_pointer_base::retire(void *obj) {
	hp_ticket *t = get_ticket();

	if (arch::get(&t->ptr) == obj)
		release();

	t->retired.push_front(obj);

	std::list<void*>::iterator it = t->retired.begin();
	while (it != t->retired.end()) {
		if (try_retire(*it))
			it = t->retired.erase(it);
		else
			it++;
	}
}

